# 在初始化完成之后，程序移动到任务0执行，并在时钟中断控制下进行任务0和1直接的切换操作
LATCH=11930
SCRN_SEL=0x18
TSS0_SEL=0x20
LDT0_SEL=0x28
TSS1_SEL=0x30
LDT1_SEL=0x38

.text
startup_32:
# 首先加载数据段寄存器DS、堆栈段寄存器SS和堆栈指针ESP。所有段的线性基地址都是0
	movl $0x10, %eax
	mov %ax, %ds
	lss init_stack, %esp
# 在新的位置重新设置IDT和GDT表
	call setup_idt
	call setup_gdt
	movl $0x10, %eax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs
	lss init_stack, %esp
# 设置8253定时芯片。把计数器通道0设置成每隔10ms向中断控制器发送一个中断请求信号
	movb $0x36, %al
	movl $0x43, %edx
	outb %al, %dx
	movl $LATCH, %eax
	movl $0x40, %edx
	outb %al, %dx
	movb %ah, %al
	outb %al, %dx
# 在IDT表第8和第128（0x80）项处分别设置定时中断门描述符和系统调用门描述符
	movl $0x00080000, %eax
	movw $timer_interrupt, %ax
	movw $0x8E00, %dx
	movl $0x08, %ecx
	lea idt(,%ecx, 8), %esi
	movl %eax, (%esi)
	movl %edx, 4(%esi)
	movw $system_interrupt, %ax
	movw $0xef00, %dx
	movl $0x80, %ecx
	lea idt(, %ecx, 8), %esi
	movl %eax, (%esi)
	movl %edx, 4(%esi)

# 好了，现在我们为移动到任务0（任务A）中执行操作堆栈内容，在堆栈中人工建立中断返回时的场景
	pushfl # 将eflags压入栈
	andl $0xffffbfff, (%esp)
	popfl # 重新写入eflags
	movl $TSS0_SEL, %eax
	ltr %ax
	movl $LDT0_SEL, %eax
	lldt %ax
	movl $0, current
	sti
	pushl $0x17
	pushl $init_stack
	pushfl
	pushl $0x0f
	pushl $task0
	ret

setup_gdt:
	lgdt ldgt_opcode
	ret

setup_idt:
	lea ignore_int, %edx
	movl $0x00080000, %eax
	movw %dx, %ax
	movw $0x8E00, %dx
	lea idt, %edi
	mov $256, %ecx
rp_idt: movl %eax, (%edi)
	movl %edx, 4(%edi)
	addl $8, %edi
	dec %ecx
	jne rp_idt
	lidt ligt_opcode
	ret

write_char:
	push %gs
	pushl %ebx
	mov $SCRN_SEL, %ebx
	mov %bx, %gs
	# 此处书上是movl scr_loc, %bx
	movl scr_loc, %ebx
	shl $1, %ebx
	incl %ebx
	cmpl $2000, %ebx
	jb 1f
	movl $0, %ebx
1:	movl %ebx, scr_loc
	popl %ebx
	pop %gs
	ret

# 以下是3个中断处理程序：默认中断，定时中断和系统调用中断
# ignore_int是默认的中断处理程序，若系统产生了其他中断，则会在屏幕上显示一个字符“C”
# 此处书上是.align 2，用高版本的as编译需要改为2^2
.align 4
ignore_int:
	push %ds
	pushl %eax
	movl $0x10, %eax
	mov %ax, %ds
	movl $67, %eax
	call write_char
	popl %eax
	pop %ds
	iret

# 这是定时中断处理程序，其中主要执行任务切换操作
# 此处书上是.align 2，用高版本的as编译需要改为2^2
.align 4
timer_interrupt:
	push %ds
	pushl %eax
	movl $0x10, %eax
	mov %ax, %ds
	movb 0x20, %al
	outb %al, $0x20
	movl $1, %eax
	cmpl %eax, current
	je 1f
	movl %eax, current
	ljmp $TSS1_SEL, $0
	jmp 2f
1: 	movl $0, current
	ljmp $TSS0_SEL, $0
2:	popl %eax
	pop %ds
	iret
# 此处书上是.align 2，用高版本的as编译需要改为2^2
.align 4
system_interrupt:
	push %ds
	pushl %edx
	pushl %ecx
	pushl %ebx
	pushl %eax
	movl $0x10, %edx
	mov %dx, %ds
	call write_char
	popl %eax
	popl %ebx
	popl %ecx
	popl %edx
	pop %ds
	iret

current: .long 0
scr_loc: .long 0

# 此处书上是.align 2，用高版本的as编译需要改为2^2
.align 4
lidt_opcode:
	.word 256 * 8 - 1
	.long idt

lgdt_opcode:
	.word (end_gdt - gdt) - 1
	.long gdt

# 此处书上是.align 3，用高版本的as编译需要改为2^3
.align 8
idt: .fill 256, 8, 0

gdt: .quad 0x0000000000000000
	.quad  0x00c09a00000007ff
	.quad  0x00c09200000007ff
	.quad  0x00c0920b80000002
	.word 0x68, tss0, 0xe900, 0x0
	.word 0x40, ldt0, 0xe200, 0x0
	.word 0x68, tss1, 0xe900, 0x0
	.word 0x40, ldt1, 0xe200, 0x0

end_gdt:
	.fill 128, 4, 0
init_stack:
	.long init_stack
	.word 0x10

# 此处书上是.align 3，用高版本的as编译需要改为2^3
.align 8
ldt0: .quad 0x0000000000000000
	.quad   0x00c0fa00000003ff
	.quad   0x00c0f200000003ff
tss0: .long 0
	.long krn_stk0, 0x10
	.long 0, 0, 0, 0, 0
	.long 0, 0, 0, 0, 0
	.long 0, 0, 0, 0, 0
	.long 0, 0, 0, 0, 0, 0
	.long LDT0_SEL, 0x8000000

	.fill 128, 4, 0

.krn_stk0:

# 此处书上是.align 3，用高版本的as编译需要改为2^3
.align 8
ldt1: .quad 0x0000000000000000
	.quad 0x00c0fa00000003ff
	.quad 0x00c0f200000003ff
tss1: .long 0
	.long krn_stk1, 0x10
	.long 0, 0, 0, 0, 0
	.long task1, 0x200
	.long 0, 0, 0, 0, 0
	.long usr_stk1, 0, 0, 0
	.long 0x17, 0x0f, 0x17, 0x17, 0x17, 0x17
	.long LDT1_SEL, 0x8000000

	.fill 128, 4, 0
krn_stk1:

task0:
	mov $0x17, %eax
	movw %ax, %ds
	# 此处书上是movl $65, 编译不通过
	movb $65, %al
	int $0x80
	movl $0xfff, %ecx
1:	loop 1b
	jmp task0

task1:
	# 此处书上是movl $66, %al, 编译不通过
	movlb $66, %al
	int $0x80
	movl $0xfff, %ecx
1:	loop 1b
	jmp task1

	.fill 128, 4, 0
usr_stk1:
